#include "helpers/general.h"
#include "helpers/exception_handler.h"
#include "helpers/factories.h"
#include "helpers/worker_ref.h"
#include "refs.h"
#include "rive/file.hpp"

#include <fcntl.h>
#include <unistd.h>

namespace ohos_rive {
long g_systemVersion = 0;

OhosRiveRenderFactory *g_RiveFactory;
OhosCanvasFactory g_CanvasFactory;

void SetSystemVersion()
{
    // 设置系统版本，这里可以根据鸿蒙API获取
    g_systemVersion = 10; // 示例值
    LOGI("System version set to: %ld", g_systemVersion);
}

uint64_t Import(napi_env env,
                uint8_t *bytes,
                int32_t length,
                RendererType rendererType,
                rive::FileAssetLoader *assetLoader)
{
    rive::ImportResult result;
    auto *fileFactory = GetFactory(rendererType);
    if (fileFactory == nullptr) {
        LOGE("fileFactory is null");
        return 0;
    }

    auto *file = rive::File::import(rive::Span<const uint8_t>(bytes, length), fileFactory, &result, assetLoader)
                     // Release the RCP to a raw pointer.
                     // We will un-ref when deleting the file.
                     .release();
    if (result == rive::ImportResult::success) {
        return reinterpret_cast<uint64_t>(file);
    } else if (result == rive::ImportResult::unsupportedVersion) {
        ThrowUnsupportedRuntimeVersionException(env, "Unsupported Rive File Version.");
        return 0;
    } else if (result == rive::ImportResult::malformed) {
        ThrowMalformedFileException(env, "Malformed Rive File.");
        return 0;
    } else {
        ThrowRiveException(env, "Unknown error loading file.");
        return 0;
    }
}

rive::Alignment GetAlignment(napi_env env, napi_value jsAlignment)
{
    int32_t alignmentValue = 0;
    napi_status status = napi_get_value_int32(env, jsAlignment, &alignmentValue);
    if (status != napi_ok) {
        LOGE("Failed to get fit enum value");
        return rive::Alignment::center;
    }

    switch (alignmentValue) {
        case 0:
            return rive::Alignment::topLeft;
        case 1:
            return rive::Alignment::topCenter;
        case 2:
            return rive::Alignment::topRight;
        case 3:
            return rive::Alignment::centerLeft;
        case 4:
            return rive::Alignment::center;
        case 5:
            return rive::Alignment::centerRight;
        case 6:
            return rive::Alignment::bottomLeft;
        case 7:
            return rive::Alignment::bottomCenter;
        case 8:
            return rive::Alignment::bottomRight;
        default:
            return rive::Alignment::center;
    }
}

rive::Fit GetFit(napi_env env, napi_value jFit)
{
    int32_t fitValue = 0;
    napi_status status = napi_get_value_int32(env, jFit, &fitValue);
    if (status != napi_ok) {
        LOGE("Failed to get fit enum value");
        return rive::Fit::none;
    }

    switch (fitValue) {
        case 0:
            return rive::Fit::none;
        case 1:
            return rive::Fit::fill;
        case 2:
            return rive::Fit::contain;
        case 3:
            return rive::Fit::cover;
        case 4:
            return rive::Fit::fitWidth;
        case 5:
            return rive::Fit::fitHeight;
        case 6:
            return rive::Fit::scaleDown;
        case 7:
            return rive::Fit::layout;
        default:
            return rive::Fit::none;
    }
}

rive::Factory *GetFactory(RendererType rendererType)
{
    LOGD("GetFactory called with renderer type: %{public}d", static_cast<int>(rendererType));

    if (rendererType == RendererType::Rive && RefWorker::RiveWorker() != nullptr) {
        LOGD("Returning Rive factory");
        return static_cast<rive::Factory *>(g_RiveFactory);
    }

    LOGD("Returning Canvas factory as fallback");
    return static_cast<rive::Factory *>(&g_CanvasFactory);
}

void DetachThread()
{
    // 鸿蒙线程分离实现
    LOGD("DetachThread called");
}

std::string NapiStringToString(napi_env env, napi_value jsString)
{
    if (jsString == nullptr) {
        return "";
    }

    size_t length = 0;
    napi_get_value_string_utf8(env, jsString, nullptr, 0, &length);

    std::string result;
    if (length > 0) {
        result.resize(length);
        napi_get_value_string_utf8(env, jsString, &result[0], length + 1, &length);
    }

    return result;
}

int32_t SizeTToInt(size_t value)
{
    return static_cast<int32_t>(value);
}

size_t NapiIntToSizeT(napi_env env, napi_value jsValue)
{
    if (jsValue == nullptr) {
        LOGW("NapiIntToSizeT() - input jsValue is null");
        return 0;
    }
    int64_t value;
    napi_status status = napi_get_value_int64(env, jsValue, &value);
    if (status != napi_ok) {
        LOGW("NapiIntToSizeT() - failed to get value");
        return 0;
    }

    if (value < 0) {
        LOGW("NapiIntToSizeT() - value is a negative number %ld", value);
        return 0;
    }

    return value > static_cast<size_t>(SIZE_MAX) ? SIZE_MAX : static_cast<size_t>(value);
}

#if defined(DEBUG) || defined(LOG)
[[noreturn]] void LogThread()
{
    int stdout_pipe[2];
    int stderr_pipe[2];

    if (pipe(stdout_pipe) == 0) {
        dup2(stdout_pipe[1], STDOUT_FILENO);
        close(stdout_pipe[1]);
    }

    if (pipe(stderr_pipe) == 0) {
        dup2(stderr_pipe[1], STDERR_FILENO);
        close(stderr_pipe[1]);
    }

    FILE *stdout_input = fdopen(stdout_pipe[0], "r");
    FILE *stderr_input = fdopen(stderr_pipe[0], "r");

    char buffer[256];
    fd_set read_fds;

    while (true) {
        FD_ZERO(&read_fds);
        if (stdout_input) {
            FD_SET(fileno(stdout_input), &read_fds);
        }
        if (stderr_input) {
            FD_SET(fileno(stderr_input), &read_fds);
        }

        int max_fd = 0;
        if (stdout_input && stderr_input) {
            max_fd = (fileno(stdout_input) > fileno(stderr_input)) ? fileno(stdout_input) : fileno(stderr_input);
        } else if (stdout_input) {
            max_fd = fileno(stdout_input);
        } else if (stderr_input) {
            max_fd = fileno(stderr_input);
        }

        int activity = select(max_fd + 1, &read_fds, nullptr, nullptr, nullptr);

        if (activity > 0) {
            if (stdout_input && FD_ISSET(fileno(stdout_input), &read_fds)) {
                if (fgets(buffer, sizeof(buffer), stdout_input)) {
                    LOGI("%{public}s", buffer);
                }
            }

            if (stderr_input && FD_ISSET(fileno(stderr_input), &read_fds)) {
                if (fgets(buffer, sizeof(buffer), stderr_input)) {
                    LOGE("%{public}s", buffer);
                }
            }
        }
    }
}
void _check_egl_error(const char *file, int line)
{
    EGLenum err(eglGetError());

    while (true) {
        std::string error;

        switch (err) {
            case EGL_SUCCESS:
                return;
            case EGL_NOT_INITIALIZED:
                error = "EGL_NOT_INITIALIZED";
                break;
            case EGL_BAD_ACCESS:
                error = "EGL_BAD_ACCESS";
                break;
            case EGL_BAD_ALLOC:
                error = "EGL_BAD_ALLOC";
                break;
            case EGL_BAD_ATTRIBUTE:
                error = "EGL_BAD_ATTRIBUTE";
                break;
            case EGL_BAD_CONTEXT:
                error = "EGL_BAD_CONTEXT";
                break;
            case EGL_BAD_CONFIG:
                error = "EGL_BAD_CONFIG";
                break;
            case EGL_BAD_CURRENT_SURFACE:
                error = "EGL_BAD_CURRENT_SURFACE";
                break;
            case EGL_BAD_DISPLAY:
                error = "EGL_BAD_DISPLAY";
                break;
            case EGL_BAD_SURFACE:
                error = "EGL_BAD_SURFACE";
                break;
            case EGL_BAD_MATCH:
                error = "EGL_BAD_MATCH";
                break;
            case EGL_BAD_PARAMETER:
                error = "EGL_BAD_PARAMETER";
                break;
            case EGL_BAD_NATIVE_PIXMAP:
                error = "EGL_BAD_NATIVE_PIXMAP";
                break;
            case EGL_BAD_NATIVE_WINDOW:
                error = "EGL_BAD_NATIVE_WINDOW";
                break;
            case EGL_CONTEXT_LOST:
                error = "EGL_CONTEXT_LOST";
                break;
            default:
                LOGE("(%d) %s - %s:%d", err, "Unknown", file, line);
                return;
        }
        LOGE("(%d) %s - %s:%d", err, error.c_str(), file, line);
        err = eglGetError();
    }
}
#endif
} // namespace ohos_rive